package jwt

import (
	"errors"
	"net/http"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
)

var (
	ErrorTokenExpired     error  = errors.New("token is expired")
	ErrorTokenNotValidYet error  = errors.New("token not active yet")
	ErrorTokenMalformed   error  = errors.New("that's not even a token")
	ErrorTokenInvalid     error  = errors.New("couldn't handle this token")
	SignKey               string = "MySignKey"
)

// Auth middleware, check the token
func Auth() gin.HandlerFunc {
	return func(context *gin.Context) {
		// Get the token in the request context
		token := context.Request.Header.Get("token")
		if token == "" {
			context.JSON(http.StatusOK, gin.H{
				"code": 10000,
				"msg":  "No token, illegal access",
			})
			context.Abort()
			return
		}

		jwt := NewJWT()
		// parseToken 解析token包含的信息
		claims, err := jwt.ParseToken(token)
		if err != nil {
			context.JSON(http.StatusOK, gin.H{
				"code": 10001,
				"msg":  err.Error(),
			})
			context.Abort()
			return
		}
		// Proceed to the next route and pass on the parsed information
		context.Set("claims", claims)
	}
}

// JWT signature structure
type JWT struct {
	SigningKey []byte
}

// Create a new JWT instance
func NewJWT() *JWT {
	return &JWT{
		SigningKey: []byte(GetSignKey()),
	}
}

// Access to signKey
func GetSignKey() string {
	return SignKey
}

//
func SetSignKey(key string) string {
	SignKey = key
	return SignKey
}

// Payloads, you can add the information you need
type CustomClaims struct {
	Name string `json:"name"`
	jwt.StandardClaims
}

// CreateToken generates a token
func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(j.SigningKey)
}

// ParseToken
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	// There is a problem with token resolution
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, ErrorTokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, ErrorTokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, ErrorTokenNotValidYet
			} else {
				return nil, ErrorTokenInvalid
			}
		}
	}
	if token != nil {
		if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
			return claims, nil
		}
	}
	return nil, ErrorTokenInvalid
}
